package com.gateway.service.runner;

import com.alibaba.fastjson.JSON;
import com.gateway.api.util.BeanUtil;
import com.gateway.service.annotation.GatewayFilterAttr;
import com.gateway.service.filter.GatewayFilter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description:
 * @author: shaoyu1
 * @date: 2019/11/4 16:02
 */
@Component
public class GatewayFilterRunner{

    private static final String basePackage = "com.gateway.service.filter";
    private static final String POST = "post";
    private static final String PRE = "pre";
    private static final String ROUTE = "route";
    private ConcurrentHashMap<String, Map<Integer,List<GatewayFilter>> > filters = new ConcurrentHashMap<>();
    /**
     * 加载所有filter
     */
    public void init(){
        Map<String, Object> beans = BeanUtil.getApplicationContext().getBeansWithAnnotation(GatewayFilterAttr.class);
        Map<Integer,List<GatewayFilter>> preFilters = new HashMap<>();
        Map<Integer,List<GatewayFilter>> postFilters = new HashMap<>();
        Map<Integer,List<GatewayFilter>> routeFilters = new HashMap<>();
        for (Object bean:beans.values()) {
            //因为是bean的缘故 bean的class是代理类的bean 而不是本身的bean 所以要父类Class
            //这里有个坑 如果是CGlib代理的话 是继承的方式实现代理 拿父class就可以了
            //如果是JDK代理的话 它实现了目标对象的同样的接口 导致拿不到本体类的class
            Class clz = bean.getClass().getSuperclass();
            GatewayFilterAttr attr = (GatewayFilterAttr)clz.getAnnotation(GatewayFilterAttr.class);
            if(attr!=null){
                if(PRE.equals(attr.type())){
                    putFilter(attr.order(),(GatewayFilter)bean,preFilters);
                }else if (POST.equals(attr.type())){
                    putFilter(attr.order(),(GatewayFilter)bean,postFilters);
                }else if (ROUTE.equals(attr.type())){
                    putFilter(attr.order(),(GatewayFilter)bean,routeFilters);
                }
            }
        }
        filters.put(PRE,preFilters);
        filters.put(POST,postFilters);
        filters.put(ROUTE,routeFilters);
    }

    private void putFilter(int order, GatewayFilter filter, Map<Integer, List<GatewayFilter>> filters) {
        if(CollectionUtils.isNotEmpty(filters.get(order))){
            filters.get(order).add(filter);
        }else{
            List l = new ArrayList<>();
            l.add(filter);
            filters.put(order,l);
        }
    }

    public Set<Class<GatewayFilter>> getClasses(String pack) {

        Set<Class<GatewayFilter>> classes = new LinkedHashSet<>();
        boolean recursive = true;
        String packageName = pack;
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClassesInPackageByFile(packageName, filePath,recursive, classes);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    public void findAndAddClassesInPackageByFile(String packageName,String packagePath,boolean recursive, Set<Class<GatewayFilter>> classes) {
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            public boolean accept(File file) {
                return (recursive && file.isDirectory())
                        || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "."
                                + file.getName(), file.getAbsolutePath(), recursive,
                        classes);
            } else {
                String className = file.getName().substring(0,
                        file.getName().length() - 6);
                try {
                    Class clz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
                    if(clz.getAnnotation(GatewayFilterAttr.class)!=null){
                        classes.add(clz);
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 执行前置过滤器
     * @throws Throwable
     */
    public void preRoute() throws Throwable {
        runFilters(PRE);
    }

    /**
     * 执行转发过滤器
     * @throws Throwable
     */
    public void route() throws Throwable{
        runFilters(ROUTE);
    }

    /**
     * 执行后置过滤器
     * @throws Throwable
     */
    public void postRoute() throws Throwable{
        runFilters(POST);
    }

    public void runFilters(String sType) throws Throwable {
        Map<Integer,List<GatewayFilter>> maps = this.filters.get(sType);
        if (MapUtils.isNotEmpty(maps)) {
            List<Integer> o = new ArrayList<>(maps.keySet());
            Collections.sort(o);
            for (int i = 0; i < o.size(); i++) {
                List<GatewayFilter> orderFilters = maps.get(o.get(i));
                if(CollectionUtils.isNotEmpty(orderFilters)){
                    for (GatewayFilter filter:orderFilters) {
                        filter.execute();
                    }
                }
            }
        }
    }

    public void init(HttpServletRequest req, HttpServletResponse resp) {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.setRequest(req);
        ctx.setResponse(resp);
        ctx.set("apiPath",req.getRequestURI());
    }
}
